#include <gtk/gtk.h>
#include "vcd-get-vm-info.h"
#include "vcd-conf/vcd-conf.h"
#include "vcd-json-parser.h"
#include "vcd-linux-ui/vcd-message-dialog.h"
#include "vcd-common.h"
#include "vcd-linux-ui/vcd-logined-window.h"
#include "vcd-util.h"
#include "vcd-json-parser.h"
#include "vcd-download.h"
#include "vcd-vm-connect.h"
#include "vcd-get-login-ticket.h"

int g_COUNT = 0;
int g_PUBLIC_COUNT = 0;
int g_virtual_list_num = 0;//这三个变量是重新画虚拟机列表需要de变量, multiple of 3, 0 3 6 9
int g_page_list_num = 3;//max value is 3

vm_info data[200];
vm_info start_sys_data;
vm_info connect_data;

typedef struct config_context config_context;

typedef struct config_context_control{
    config_context* ctx;
    int count_alloc;
    int count_valid;
    vm_info *head_info;
}config_context_control;

typedef struct node_status{
    char node[60];
    char status[30];
}node_status;

typedef struct config_context{
    vm_info *info;
    int is_reponse;
    int is_deal_ok;
    config_context_control* parent;
}config_context;

static int get_valid_vm(vm_info* info, int count, config_context_control** pp_ctrl);
static void prepare_for_curl(char *url, char *ip_port,char* node,int vmid);
static char* get_best_match_ip(char** pp_ip, int count, char* dip);
static gboolean get_vm_func(gpointer data);
static gboolean get_vm_config_func(gpointer data);
static void process_is_all_config_response(config_context_control * ctrl);
static int get_all_vm_config(config_context_control * ctrl);

static void config_context_control_free(config_context_control **pp_ctrl);

static void config_context_control_free(config_context_control **pp_ctrl)
{
    if (pp_ctrl == NULL || *pp_ctrl == NULL) {
        return;
    }

    config_context_control *ctrl = *pp_ctrl;
    g_free(ctrl->head_info);
    g_free(ctrl->ctx);
    g_free(ctrl);
    *pp_ctrl = NULL;
}

static void prepare_for_curl(char *url, char *ip_port,char* node,int vmid)
{
    char stringvmid[10];
    sprintf(stringvmid, "%d", vmid);

    strcpy(url, "https://");
    strcat(url, ip_port);
    strcat(url, "/api2/extjs/nodes/");

    strcat(url, node);
    strcat(url, "/qemu/");
    strcat(url, stringvmid);
    strcat(url, "/spiceproxy");
}

static 
char* get_best_match_ip(char** pp_ip, int count, char* dip)
{
    uint32_t v1, v2;
    int i = 0;
    unsigned char buf[sizeof(struct in6_addr)];
    memset(buf, 0, sizeof(buf));
    int res = inet_pton(AF_INET, dip, buf);
    if (res != 1)
        return NULL;

    int circle_cnt = 1;

    while(1) {
        for(i = 0; i < count; ++i) {
            M_DEBUG("num [%d] active server ip [%s]", i + 1, pp_ip[i]);
            unsigned char bf[sizeof(struct in6_addr)];
            res = inet_pton(AF_INET, pp_ip[i], bf);
            M_DEBUG("linux==> strptr=%s,addrptr=%s\n", pp_ip[i], bf);
            if (res != 1)
                continue;
            v1 = (*(uint32_t*)buf);
            v2 = (*(uint32_t*)bf);
            v1 <<= 8 * circle_cnt;
            v2 <<= 8 * circle_cnt;
            if(v1 == v2){
                return strdup(pp_ip[i]);
            }
        }

        ++circle_cnt;
        if (circle_cnt > 3) {
            break;
        }

    }
    return strdup(dip);
}

gboolean curl_get_server_ip_callback_task(gpointer data)
{
    char** pp_ip  = NULL;
    int count = 0;
    char post[1000] = {0};
    struct curl_slist *list = NULL;
    char url[480];
    char* best_ip = NULL;
    struct network_request_arg *network_task = NULL;
    struct network_response* res = (struct network_response*)data;

    if (data == NULL)
        return FALSE;

    char *memory = (char*)res->memory;
    if (!memory)
        goto fail;

    if (!json_parser_get_server_ip(memory, &pp_ip, &count))
        goto fail;

    if(g_vcd_conf->local_model_check_status){
        best_ip = strdup("127.0.0.1");
    }else{
        best_ip = get_best_match_ip(pp_ip, count, g_vcd_conf->ip);
        if (best_ip == NULL)
        goto fail;
    }

    strncpy(connect_data.server_ip, best_ip, sizeof(connect_data.server_ip)-1);
    SAFE_FREE(best_ip);

    list = curl_slist_append(list, g_final_ticket);
    list = curl_slist_append(list, g_token);
    prepare_for_curl(url, get_ip_port(),connect_data.node,connect_data.vmid);

    strcpy(post, "proxy=");
    M_DEBUG("server_ip = %s", connect_data.server_ip);
    strcat(post, connect_data.server_ip);//spiceproxy api里面加一个proxy=server_ip的参数就可以在返回的proxy里面得到这个ip，这个就是我们需要填写的host

    network_task = (struct network_request_arg *)malloc(sizeof(struct network_request_arg));
    network_task->timeout = 3L;
    network_task->list = list;
    network_task->data_priv = NULL;
    network_task->http_method = CURLOPT_POST;
    network_task->callback = button_enter_callback_task;
    strcpy(network_task->post_fields, post);
    strcpy(network_task->url, url);

    net_req_list_add(network_task);

    network_response_free(&res);
    return FALSE;

fail:
    //message_box("获取虚拟机所在物理机ip失败!");
    delete_g_connect_label_and_spinner();
    network_response_free(&res);
    M_DEBUG("curl_get_server_ip_callback_task fail!!!");
    return FALSE;
}

static int get_valid_vm(vm_info* info, int count, config_context_control** pp_ctrl)
{
    int i = 0,j = 0;
    int node_count = 0;
    int valid_pos = 0;

    config_context_control *ctrl = (config_context_control*)g_malloc0(sizeof(config_context_control));
    if (ctrl == NULL)
        return -1;

    config_context* ctx = (config_context*)g_malloc0(sizeof(config_context) * count);
    if (ctx == NULL) {
        g_free(ctrl);
        ctrl = NULL;
        return -1;
    }

    //record node status
    j = 0;
    node_status *ns= (node_status*)g_malloc0(count * sizeof(node_status));
    for (i = 0; i < count; ++i) {
        vm_info* f = info + i;
        if (strcmp("node", f->type) == 0) {
            node_status *p = ns + node_count;
            strncpy(p->node, f->node, sizeof(p->node));
            strncpy(p->status, f->status, sizeof(p->status));
            ++node_count;
        }
    }

    for (i = 0; i< count; ++i) {
        vm_info* f = info + i;

        //首先判断类型，以下几种是要排除的
        if (strcmp("storage", f->type) == 0  ||
                strcmp("node", f->type) == 0  ||
                strcmp("lxc", f->type) == 0  ||
                strcmp("pool", f->type) == 0) {
            //M_DEBUG("type=%s", f->type);
            continue;
        }

        //judge node is offline
        gboolean online = TRUE;
        for(j = 0; j < node_count; ++j){
            node_status *p = ns + j;
            if (strcmp(f->node, p->node) == 0) {
                if (strcasecmp(p->status, "offline") == 0) {
                    online = FALSE;
                    break;
                }
            }
        }

        //exclude node offline
        if (online != TRUE)
            continue;

        //这句话不能写在上面，因为上面一个判断如果成立的话就不会有template这个字段（不是正常的虚拟机不会有这个字段）
        if (f->Template != 0 || strcmp(f->type, "qemu") != 0) {//template=1的是模板要排除，而且type要等于qemu才行
            //M_DEBUG("Template=%d, type=%s", f->Template, f->type);
            continue;
        }

        ctx[valid_pos].is_reponse = 0;
        ctx[valid_pos].info = f;
        ctx[valid_pos].parent = ctrl;
        ctx[valid_pos].is_deal_ok = 0;
        ++valid_pos;
    }

    //没有可用的虚拟机
    if (valid_pos == 0) {
        M_DEBUG("vm count = 0");
        goto fail;
    }

    //M_DEBUG("vm count = %d", count);

    ctrl->ctx = ctx;
    ctrl->count_alloc = count;
    ctrl->head_info = info;
    ctrl->count_valid = valid_pos;

    *pp_ctrl = ctrl;
    G_SAFE_FREE(ns);
    return 0;

fail:
    G_SAFE_FREE(ns);
    G_SAFE_FREE(ctrl);
    G_SAFE_FREE(ctx);
    return -1;
}

static void process_is_all_config_response(config_context_control * ctrl)
{
    for (int i = 0; i < ctrl->count_valid; ++i) {
        config_context *ctx = ctrl->ctx + i;
        if(ctx->is_reponse == 0)
            return;
    }

    int deal_ok_count = 0;

    /*先将结构体数组清零，虚拟机数量置零*/
    clean_vm_data();

    //all response finished
    for(int i = 0; i < ctrl->count_valid; ++i) {
        config_context *ctx = ctrl->ctx + i;
        if (ctx->is_deal_ok) {
            memcpy(data + deal_ok_count, ctx->info, sizeof(vm_info));
            ++deal_ok_count;
            //M_DEBUG("process_is_all_config_response vmid:%d,node:%s,server_ip:%s",ctx->info->vmid,ctx->info->node,ctx->info->server_ip);
        }
    }

    //sort by name
    sort_vm_info(data, deal_ok_count);

    g_COUNT = deal_ok_count;

    //control free
    config_context_control_free(&ctrl);

    //刷新虚拟机列表
    fresh_vm_list();
}

static gboolean get_vm_config_func(gpointer data)
{
    g_return_val_if_fail(data != NULL, FALSE);
    config_context *ctx = NULL;
    config_context_control * ctrl = NULL;
    vm_config_response r;
    vm_info *f = NULL;
    int ret = 0;
    network_response *res = (network_response*)data;
    memset(&r, 0, sizeof(vm_config_response));

    ctx = (config_context *)res->data_priv;
    ctrl = ctx->parent;
    f = ctx->info;

    ctx->is_reponse = 1;

    if (res->memory == NULL)
        goto fail;

    ret = json_parser_get_spice_config(res->memory, &r);
    if (ret != 0)
        goto fail;

    if (r.ostype)
        strncpy(f->system_type, r.ostype, sizeof(f->system_type));

    if (r.net0)
        strncpy(f->net0, r.net0, sizeof(f->net0));

    ctx->is_deal_ok = 1;
fail:
    vm_config_response_free(&r);
    process_is_all_config_response(ctrl);
    network_response_free(&res);
    return FALSE;
}

static int get_all_vm_config(config_context_control * ctrl)
{
    if (ctrl == NULL || ctrl->count_valid < 1)
        return -1;

    int i = 0;
    network_request_arg *req_arr[ctrl->count_valid];

    for (i = 0; i < ctrl->count_valid; ++i) {
        config_context* ctx = ctrl->ctx + i;
        vm_info *f = ctx->info;

        struct curl_slist *list = NULL;

        list = curl_slist_append(list, g_final_ticket);
        list = curl_slist_append(list, g_token);

        char url[256];
        snprintf(url, sizeof(url),"https://%s/api2/json/nodes/%s/qemu/%d/config",
                 get_ip_port(), f->node, f->vmid);

        network_request_arg *req = (network_request_arg *)malloc(sizeof(network_request_arg));
        if (req == NULL)
           goto fail;

        req_arr[i] = req;

        strncpy(req->url, url, sizeof(req->url));
        req->http_method = CURLOPT_HTTPGET;
        memset(req->post_fields, 0, sizeof(req->post_fields));
        req->timeout = 3;
        req->list = list;
        req->data_priv = ctx;
        req->callback = get_vm_config_func;
    }

    //add to list
    for (int j = 0; j < i; ++j) {
        network_request_arg* req = *(req_arr + j);
        net_req_list_add(req);
    }

    return 0;

fail:
    for (int j = 0; j < i; ++j) {
        network_request_arg* req = *(req_arr + j);
        if (req->list)
            curl_slist_free_all(req->list);
        SAFE_FREE(req);
    }
    return -1;
}

void clean_vm_data(void)
{
    memset(data, 0, sizeof(data));
    g_COUNT = 0;
    g_PUBLIC_COUNT = 0;
}

static gboolean get_vm_func(gpointer data)
{
    int count = 0;
    int ret = 0;
    char tmp_error_msg[1024] = {0};
    vm_info *info = NULL;
    config_context_control *ctrl = NULL;
    struct network_response *res = (struct network_response *)data;
    if (data == NULL) {
        //message_box("获取虚拟机信息失败");
        goto fail;
    }

    if (res->memory == NULL) {
        if (res->error_msg) {
            char szbuf[255];
            snprintf(szbuf, sizeof(szbuf), "获取虚拟机信息失败:%s", res->error_msg);
            //message_box(szbuf);
        }
        goto fail;
    }    

    ret = json_parser_get_vm_info(res->memory, &info, &count, tmp_error_msg);
    if (ret != 0) {
        message_box(tmp_error_msg);
        goto fail;
    }

    ret = get_valid_vm(info, count, &ctrl);
    if (ret != 0) {
        message_box("没有可连接的虚拟机");
        goto fail;
    }

    ret = get_all_vm_config(ctrl);
    if (ret != 0) {
        message_box("获取虚拟机操作系统失败");
        goto fail;
    }

    network_response_free(&res);
    return FALSE;

fail:
    //exit_logined_window();
    G_SAFE_FREE(info);
    network_response_free(&res);
    config_context_control_free(&ctrl);
    return FALSE;
}


int get_vm_list(void)
{
    struct vcd_conf* tmp_vcd_conf = g_vcd_conf;
    g_return_val_if_fail(tmp_vcd_conf != NULL,0);
    
    struct curl_slist *list = NULL;
    char url[200] = "https://";

    strcat(url, get_ip_port());

    //example url2:"https://192.168.10.221:8006/api2/json/cluster/resources"
    if (tmp_vcd_conf->local_model_check_status) {
        strcat(url, "/api2/json/cluster/resources");
    }
    else if (EMODE_NEW == tmp_vcd_conf->login_mode) {
        list = curl_slist_append(list, g_new_resources_ticket);
        strcat(url, "/api/server/userResources");
    } else {
        list = curl_slist_append(list, g_final_ticket);
        list = curl_slist_append(list, g_token);
        strcat(url, "/api2/json/cluster/resources");
    }

    network_request_arg *req = (network_request_arg*)malloc(sizeof(network_request_arg));
    if (req == NULL)
        return -1;

    memset(req, 0, sizeof(network_request_arg));
    strncpy(req->url, url, sizeof(req->url));
    req->http_method = CURLOPT_HTTPGET;
    req->timeout = 3;
    req->list = list;
    req->callback = get_vm_func;
    net_req_list_add(req);
    return 0;
}

void get_record_data_and_connect(void)
{
    M_DEBUG("get_record_data_and_connect");
    struct vcd_conf* tmp_vcd_conf = g_vcd_conf;

    vm_info tmp_connect_data;
    memset(&tmp_connect_data, 0, sizeof(vm_info));
    #if 0
    /*如果字符串初始值为空，也没关系，拷贝过后仍为空*/
    // strncpy(tmp_connect_data.node, tmp_vcd_conf->node, sizeof(tmp_connect_data.node));
    // tmp_connect_data.vmid = tmp_vcd_conf->vmid;
    // strncpy(tmp_connect_data.public_id, tmp_vcd_conf->public_id, sizeof(tmp_connect_data.public_id));
    #else
    memcpy(&tmp_connect_data,&connect_data,sizeof(vm_info));
    #endif
    
    /*自动连接虚拟机*/
    button_enter(&tmp_connect_data);
}

int get_local_vm_list(void)
{
    struct vcd_conf* tmp_vcd_conf = g_vcd_conf;
    g_return_val_if_fail(tmp_vcd_conf != NULL,0);


    //char url[200] = "https://127.0.0.1:8006/api2/json/nodes/v-b6vxxy47/qemu";
    char url[200] = "https://127.0.0.1:8006/api2/json/cluster/resources";

    struct curl_slist *list = NULL;
//    list = curl_slist_append(list, "Authorization: PVEAPIToken=root@pam!idv=cce8e255-0b3e-4ef0-84a0-85cfe32ed3f7");

    network_request_arg *req = (network_request_arg*)malloc(sizeof(network_request_arg));
    if (req == NULL)
        return -1;

    memset(req, 0, sizeof(network_request_arg));
    strncpy(req->url, url, sizeof(req->url));
    req->http_method = CURLOPT_HTTPGET;
    req->timeout = 3;
    req->list = list;
    req->callback = get_vm_func;
    net_req_list_add(req);
    return 0;
}
